/**
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.xume.raob.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.xume.raob.ObjectBuilder;
import org.xume.raob.exceptions.ObjectBuilderConfigurationException;
import org.xume.raob.exceptions.InstatiationException;

/**
 * @author Johan Siebens
 */
@SuppressWarnings("unchecked")
public class ReflectionUtils {

	public static final Method BUILD_METHOD = buildMethod();

	public static Method buildMethod() {
		try {
			return ObjectBuilder.class.getDeclaredMethod("build");
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	public static <T> T createInstance(Class<T> targetClass) {
		try {
			Constructor<T> constructor = targetClass.getDeclaredConstructor();
			if (!constructor.isAccessible()) {
				constructor.setAccessible(true);
			}
			return constructor.newInstance();
		} catch (NoSuchMethodException e) {
			throw new InstatiationException("failed to create instance of [" + targetClass.getName() + "]: no default constructor found",
					e);
		} catch (Exception e) {
			throw new InstatiationException("failed to create instance of [" + targetClass.getName() + "]", e);
		}
	}

	public static Method findMethod(Class<?> targetClass, String methodName, Class<?>[] parameterTypes) {
		try {
			return targetClass.getMethod(methodName, parameterTypes);
		} catch (NoSuchMethodException e) {
			throw new ObjectBuilderConfigurationException("No match found for method [" + methodName + "] in targetClass class ["
					+ targetClass + "]", e);
		}
	}

	public static boolean isBuildMethod(Method method) {
		return BUILD_METHOD.equals(method);
	}

	public static <T> Class<T> targetClass(Class<? extends ObjectBuilder<? extends T>> builderInterface) {
		Type[] genericInterfaces = builderInterface.getGenericInterfaces();
		for (Type type : genericInterfaces) {
			ParameterizedType pt = (ParameterizedType) type;
			if (ObjectBuilder.class.equals(pt.getRawType())) {
				return (Class<T>) pt.getActualTypeArguments()[0];
			}
		}
		throw new ObjectBuilderConfigurationException("Failed to locate target class for builder [" + builderInterface + "]");
	}
}
